import * as ol from "ol";
import * as keys from "./keys";
import * as Layer from "ol/layer";
import * as Source from "ol/source";
import RenderFeature from "ol/render/Feature";
import { Geometry, Point } from "ol/geom";
import { MapEventCallback, MapEventType } from "./types";
import { _tileGrid } from "./tianditu";
import { parseLngLat } from "./utils";
import { panTo } from "./view";
import { log } from "./log";
import { List } from "./list";

// 地图实例
export let mapInstance: ol.Map | null = null;
// 地图鼠标移动延时
let mapPointermoveTimeout: ReturnType<typeof setTimeout> | undefined;
// 地图鼠标聚焦元素
const mapFocusFeatures = new List<ol.Feature>();
// 地图事件回调
const mapEventCallback = new Map<string, MapEventCallback[]>([
  ["click", []],
  ["dblclick", []],
]);

export const getMap = (): ol.Map | null => {
  return mapInstance;
};

export const openlayersInit = (map: ol.Map): void => {
  if (map instanceof ol.Map) {
    mapInstance = map;
    // 标记默认图层不予删除
    map.getAllLayers().forEach((layer) => {
      layer.set(keys.CLEARABLE_KEY, false);
      layer.set(keys.IS_BASE_LAYER_KEY, true);
    });
    const defaultFeatureLayer = new Layer.Vector({
      source: new Source.Vector(),
    });
    defaultFeatureLayer.set(keys.LAYER_TYPE_KEY, "default-feature-layer");
    map.addLayer(defaultFeatureLayer);
    const hiddenFeatureLayer = new Layer.Vector({
      source: new Source.Vector(),
      visible: false,
    });
    hiddenFeatureLayer.set(
      keys.LAYER_TYPE_KEY,
      "default-feature-layer--hidden"
    );
    map.addLayer(hiddenFeatureLayer);
    map.on("click", mapEventHandler);
    map.on("dblclick", mapEventHandler);
    map.on("pointermove", mapEventHandler);
  }
};
export const destroyMap = () => {
  if (mapInstance instanceof Map) {
    mapInstance.un("click", mapEventHandler);
    mapInstance.un("dblclick", mapEventHandler);
    mapInstance.un("pointermove", mapEventHandler);
    mapInstance = null;
  }
};
/**
 * @description: 地图事件处理
 * @private
 * @param {ol.MapBrowserEvent} e 地图事件
 * @returns
 */
const mapEventHandler = (e: ol.MapBrowserEvent<any>): void => {
  if (!mapInstance) {
    return;
  }
  if (["click", "dblclick"].includes(e.type)) {
    mapClickHandler(e);
  } else if (e.type === "pointermove") {
    if (mapPointermoveTimeout) {
      clearTimeout(mapPointermoveTimeout);
      mapPointermoveTimeout = undefined;
    }
    mapPointermoveTimeout = setTimeout(() => {
      mapMoveHandler(e);
    }, 50);
  }
};

/**
 * @description: 地图点击处理
 * @private
 * @param {ol.MapBrowserEvent} e 地图事件
 * @returns
 */
const mapClickHandler = ({
  originalEvent,
  type,
}: ol.MapBrowserEvent<any>): void => {
  if (!mapInstance) {
    return;
  }

  const pixel = mapInstance.getEventPixel(originalEvent);

  const featuresAtPixel: ol.Feature[] = [];
  mapInstance.forEachFeatureAtPixel(
    pixel,
    (feature: RenderFeature | ol.Feature<Geometry>) => {
      if (feature instanceof ol.Feature) {
        featuresAtPixel.push(feature);
      }
    }
  );

  // 地图事件触发
  if (mapEventCallback.has(type) && Array.isArray(mapEventCallback.get(type))) {
    const callbacks = mapEventCallback.get(type) ?? [];
    if (callbacks.length > 0) {
      callbacks.forEach((callback) => {
        callback.call(null, { map: mapInstance, featuresAtPixel, pixel, type });
      });
    }
  }

  if (type === "click") {
    // 触发地图调试点击事件
    const coordinate = mapInstance.getEventCoordinate(originalEvent);
    const [matrix, tileCol, tileRow] =
      _tileGrid.getTileCoordForCoordAndResolution(
        coordinate,
        mapInstance.getView().getResolution() as number
      );
    log("Map Click", {
      x: pixel[0],
      y: pixel[1],
      longitude: coordinate[0],
      latitude: coordinate[1],
      zoom: mapInstance.getView().getZoom(),
      tileRow,
      tileCol,
      matrix,
    });
    // 元素事件触发
    for (let index = 0; index < featuresAtPixel.length; index++) {
      const feature = featuresAtPixel[index];
      const featureType = feature.get(keys.FEATURE_TYPE_KEY);
      const clusters = feature.get(keys.CLUSTERS_KEY) ?? [];
      const stopClick = feature.get(keys.STOP_CLICK_KEY) ?? true;
      const onClick = feature.get(keys.ON_CLICK_KEY);

      // 点聚合点位自动放大层级
      if (featureType === "cluster-controller") {
        if (
          (mapInstance.getView().getZoom() ?? 0) <
          mapInstance.getView().getMaxZoom()
        ) {
          panTo({
            center: parseLngLat(
              (feature.getGeometry() as Point).getCoordinates()
            ),
            zoom: Math.floor(
              ((mapInstance as ol.Map).getView().getZoom() ?? 0) + 1
            ),
          });
          log("Cluster Controller Click", {
            map: mapInstance,
            feature,
            featuresAtPixel,
            clusters,
            pixel,
            type,
          });
        } else if (typeof onClick === "function") {
          onClick.call(null, feature.get(keys.FEATURE_EXTRA_DATA_KEY) ?? {}, {
            map: mapInstance,
            feature,
            featuresAtPixel,
            clusters,
            pixel,
            type,
          });
        }
      } else if (typeof onClick === "function") {
        onClick.call(null, feature.get(keys.FEATURE_EXTRA_DATA_KEY) ?? {}, {
          map: mapInstance,
          feature,
          featuresAtPixel,
          clusters,
          pixel,
          type,
        });
      }

      if (stopClick) {
        break;
      }
    }
  } else if (type === "dblclick") {
    // 元素事件触发
    for (let index = 0; index < featuresAtPixel.length; index++) {
      const feature = featuresAtPixel[index];
      const clusters = feature.get(keys.CLUSTERS_KEY) ?? [];
      const stopDBClick = feature.get(keys.STOP_DBCLICK_KEY) ?? true;
      const onDBClick = feature.get(keys.ON_DBCLICK_KEY);

      if (typeof onDBClick === "function") {
        onDBClick.call(null, feature.get(keys.FEATURE_EXTRA_DATA_KEY) ?? {}, {
          map: mapInstance,
          feature,
          featuresAtPixel,
          clusters,
          pixel,
          index,
          type: "dbclick",
        });
      }

      if (stopDBClick) {
        break;
      }
    }
  }
};
/**
 * @description: 地图移动处理
 * @private
 * @param {ol.MapBrowserEvent} e 地图事件
 * @returns
 */
const mapMoveHandler = ({ originalEvent }: ol.MapBrowserEvent<any>): void => {
  if (!mapInstance) {
    return;
  }
  const pixel = mapInstance.getEventPixel(originalEvent);

  const featuresAtPixel: ol.Feature[] = [];
  mapInstance.forEachFeatureAtPixel(
    pixel,
    (feature: RenderFeature | ol.Feature<Geometry>) => {
      if (feature instanceof ol.Feature) {
        featuresAtPixel.push(feature);
      }
    }
  );

  const mapFocusFeatureIds = mapFocusFeatures
    .toArray()
    .map((feature: any) => Reflect.get(feature, "ol_uid"));
  const featuresAtPixelIds = featuresAtPixel.map((feature) =>
    Reflect.get(feature, "ol_uid")
  );

  // 鼠标移入元素列表
  const mouseEnterFeatures: ol.Feature[] = featuresAtPixel.filter(
    (feature) => !mapFocusFeatureIds.includes(Reflect.get(feature, "ol_uid"))
  );
  // 鼠标移出元素列表
  const mouseLeaveFeatures: ol.Feature[] = mapFocusFeatures
    .toArray()
    .filter(
      (feature: any) =>
        !featuresAtPixelIds.includes(Reflect.get(feature, "ol_uid"))
    );

  // 移入元素事件触发
  for (let index = 0; index < mouseEnterFeatures.length; index++) {
    const feature = mouseEnterFeatures[index];
    const clusters = feature.get(keys.CLUSTERS_KEY) ?? [];
    const onMouseEnter = feature.get(keys.ON_MOUSE_ENTER_KEY);

    if (typeof onMouseEnter === "function") {
      onMouseEnter.call(null, feature.get(keys.FEATURE_EXTRA_DATA_KEY) ?? {}, {
        map: mapInstance,
        feature,
        featuresAtPixel,
        clusters,
        index,
        pixel,
        type: "mouseenter",
      });
    }
  }

  // 移出元素事件触发
  for (let index = 0; index < mouseLeaveFeatures.length; index++) {
    const feature = mouseLeaveFeatures[index];
    const clusters = feature.get(keys.CLUSTERS_KEY) ?? [];
    const onMouseLeave = feature.get(keys.ON_MOUSE_LEAVE_KEY);

    if (typeof onMouseLeave === "function") {
      onMouseLeave.call(null, feature.get(keys.FEATURE_EXTRA_DATA_KEY) ?? {}, {
        map: mapInstance,
        feature,
        featuresAtPixel,
        clusters,
        pixel,
        type: "mouseleave",
      });
    }
  }

  mapFocusFeatures.addAll(mouseEnterFeatures);
  mapFocusFeatures.removeAll(mouseLeaveFeatures);
};
/**
 * @description: 添加地图事件监听
 * @param {string} type 事件类型
 * @param {MapEventCallback} callback 事件回调
 * @returns
 */
export const addMapEventListener = (
  type: MapEventType,
  callback: MapEventCallback
) => {
  if (mapEventCallback.has(type)) {
    mapEventCallback.get(type)?.push(callback);
  }
};
/**
 * @description: 移除地图事件监听
 * @param {string} type 事件类型
 * @param {MapEventCallback} callback 事件回调
 * @returns
 */
export const removeMapEventListener = (
  type: MapEventType,
  callback: MapEventCallback
) => {
  if (mapEventCallback.has(type)) {
    mapEventCallback.get(type)?.push(callback);
  }
};

/**
 * @description: 清空地图事件监听
 * @returns
 */
export const clearMapEventListener = () => {
  for (const key of mapEventCallback.keys()) {
    mapEventCallback.set(key, []);
  }
};
